import React from 'react';
import { MongoDBInstance } from 'mongodb-instance-model';
import type { RenderWithConnectionsResult } from '@mongodb-js/testing-library-compass';
import {
  render,
  screen,
  cleanup,
  waitFor,
  userEvent,
} from '@mongodb-js/testing-library-compass';
import { expect } from 'chai';
import { CollectionsWorkspaceTab } from './';
import Sinon from 'sinon';
import {
  type PreferencesAccess,
  createSandboxFromDefaultPreferences,
} from 'compass-preferences-model';

describe('Collections [Plugin]', function () {
  let dataService: any;
  let mongodbInstance: Sinon.SinonSpiedInstance<MongoDBInstance>;
  let appRegistry: Sinon.SinonSpiedInstance<
    RenderWithConnectionsResult['globalAppRegistry']
  >;
  let preferences: PreferencesAccess;

  beforeEach(async function () {
    preferences = await createSandboxFromDefaultPreferences();
    mongodbInstance = Sinon.spy(
      new MongoDBInstance({
        databases: [
          {
            _id: 'foo',
            name: 'foo',
            collections: [],
          },
        ],
        topologyDescription: { type: 'ReplicaSetWithPrimary' },
        preferences,
      } as any)
    );
    for (const db of mongodbInstance.databases) {
      Sinon.spy(db);
    }

    dataService = {
      listCollections() {
        return Promise.resolve([
          { _id: 'foo.bar', name: 'bar' },
          { _id: 'foo.buz', name: 'buz' },
        ]);
      },
    };
  });

  afterEach(function () {
    mongodbInstance.removeAllListeners();
    Sinon.resetHistory();
    cleanup();
  });

  describe('with loaded collections', function () {
    before(() => {
      process.env.COMPASS_DISABLE_VIRTUAL_TABLE_RENDERING = 'true';
    });

    beforeEach(async function () {
      const Plugin = CollectionsWorkspaceTab.provider.withMockServices({
        instance: mongodbInstance,
        database: mongodbInstance.databases.get('foo'),
        dataService,
      });

      const { globalAppRegistry } = render(
        <Plugin namespace="foo">
          <CollectionsWorkspaceTab.content namespace="foo" />
        </Plugin>
      );
      appRegistry = Sinon.spy(globalAppRegistry);

      await waitFor(() => {
        expect(screen.getByTestId('collections-list-row-bar')).to.exist;
        expect(screen.getByTestId('collections-list-row-buz')).to.exist;
      });
    });

    it('renders a list of collections', function () {
      const list = screen.getByTestId('collections-list');
      expect(
        list.querySelectorAll('[data-lgid="lg-table-row"]')
      ).to.have.lengthOf(2);
    });

    it('initiates action to create a collection', function () {
      userEvent.click(
        screen.getByRole('button', { name: /Create collection/ })
      );
      expect(appRegistry.emit).to.have.been.calledWithMatch(
        'open-create-collection',
        { ns: 'foo' },
        // this event is supposed to emit always with a connectionId and this
        // connection id is the default provided by the connectionInfoProvider
        { connectionId: 'TEST' }
      );
    });

    it('initiates action to refresh collections', function () {
      userEvent.click(screen.getByRole('button', { name: /Refresh/ }));
      // eslint-disable-next-line @typescript-eslint/unbound-method
      expect(mongodbInstance.databases.get('foo')?.fetchCollectionsDetails).to
        .have.been.called;
    });

    it('initiates action to drop a collection', function () {
      const row = screen.getByTestId('collections-list-row-bar');
      userEvent.hover(row);
      userEvent.click(
        row.querySelector('[aria-label="Delete bar"]') as Element
      );
      expect(appRegistry.emit).to.have.been.calledWithMatch(
        'open-drop-collection',
        { ns: 'foo.bar' },
        // this event is supposed to emit always with a connectionId and this
        // connection id is the default provided by the connectionInfoProvider
        { connectionId: 'TEST' }
      );
    });

    it('updates when instance model updates', async function () {
      (mongodbInstance.databases.get('foo') as any).set({
        collections: [{ _id: 'foo.testdb', name: 'testdb' }],
      });

      await waitFor(() => {
        expect(screen.queryByTestId('collections-list-row-bar')).to.not.exist;
        expect(screen.getByTestId('collections-list-row-testdb')).to.exist;
      });

      expect(screen.getByRole('button', { name: /Create collection/ })).to
        .exist;

      (mongodbInstance as any).set({
        topologyDescription: { type: 'Unknown' },
      });

      expect(screen.queryByRole('button', { name: /Create collection/ })).to.not
        .exist;
    });
  });
});
